package com.easymeeting.websocket;

import com.alibaba.fastjson.JSON;
import com.easymeeting.entity.dto.MeetingExitDto;
import com.easymeeting.entity.dto.MeetingMemberDto;
import com.easymeeting.entity.dto.MessageSendDto;
import com.easymeeting.entity.dto.TokenUserInfoDto;
import com.easymeeting.entity.enums.MeetingMemberStatusEnum;
import com.easymeeting.entity.enums.MessageSend2TypeEnum;
import com.easymeeting.entity.enums.MessageTypeEnum;
import com.easymeeting.entity.po.UserInfo;
import com.easymeeting.entity.query.UserInfoQuery;
import com.easymeeting.mappers.UserInfoMapper;
import com.easymeeting.redis.RedisComponet;
import com.easymeeting.utils.JsonUtils;
import com.easymeeting.utils.StringTools;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

@Component("channelContextUtils")
@Slf4j
public class ChannelContextUtils {

    @Resource
    private RedisComponet redisComponet;

    @Resource
    private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;

    public static final ConcurrentMap<String, Channel> USER_CONTEXT_MAP = new ConcurrentHashMap();

    public static final ConcurrentMap<String, ChannelGroup> MEETING_ROOM_CONTEXT_MAP = new ConcurrentHashMap();

    public void sendMessage(MessageSendDto messageSendDto) {
        if (MessageSend2TypeEnum.USER.getType().equals(messageSendDto.getMessageSend2Type())) {
            sendMsg2User(messageSendDto);
        } else {
            sendMsg2Group(messageSendDto);
        }
    }

    private void sendMsg2Group(MessageSendDto messageSendDto) {
        if (messageSendDto.getMeetingId() == null) {
            return;
        }
        ChannelGroup Group = MEETING_ROOM_CONTEXT_MAP.get(messageSendDto.getMeetingId());
        if (Group == null) {
            return;
        }
        Group.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(messageSendDto)));
        if(MessageTypeEnum.EXIT_MEETING_ROOM.equals(messageSendDto.getMessageType())){
            removeContextFromGroup(messageSendDto.getSendUserId(), messageSendDto.getMeetingId());
            List<MeetingMemberDto> meetingMemberDtos=redisComponet.getMeetingMemberList(messageSendDto.getMeetingId());
            List<MeetingMemberDto> onlineList=meetingMemberDtos.stream().
                    filter(meetingMemberDto -> meetingMemberDto.getStatus().equals(MeetingMemberStatusEnum.NORMAL.getStatus()))
                    .collect(Collectors.toList());
            if(onlineList.isEmpty()){
                removeContextGroup(messageSendDto.getMeetingId());
            }
            return;
        }
        if(MessageTypeEnum.FINIS_MEETING.equals(messageSendDto.getMessageType())){
            List<MeetingMemberDto> meetingMemberDtos=redisComponet.getMeetingMemberList(messageSendDto.getMeetingId());
            for (MeetingMemberDto meetingMemberDto:meetingMemberDtos){
                    removeContextFromGroup(meetingMemberDto.getUserId(), messageSendDto.getMeetingId());
            }
        }
    }

    private void sendMsg2User(MessageSendDto messageSendDto) {
        if (messageSendDto.getReceiveUserId() == null) {
            return;
        }
        Channel channel = USER_CONTEXT_MAP.get(messageSendDto.getReceiveUserId());
        if (channel == null) {
            return;
        }
        channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(messageSendDto)));
    }

    public void closeContext(String userId) {
        if (StringTools.isEmpty(userId)) {
            return;
        }
        Channel channel = USER_CONTEXT_MAP.get(userId);
        USER_CONTEXT_MAP.remove(userId);
        if (channel != null) {
            channel.close();
        }
    }


    //用户移除群
    public void removeContextFromGroup(String userId, String meetingId) {
        Channel channel = USER_CONTEXT_MAP.get(meetingId);
        if(channel==null){
            return;
        }
        ChannelGroup groupChannel = MEETING_ROOM_CONTEXT_MAP.get(meetingId);
        if(groupChannel!=null){
            groupChannel.remove(channel);
        }
    }

    public void removeContextGroup(String meetingId) {
        MEETING_ROOM_CONTEXT_MAP.remove(meetingId);
    }

    public void removeContextUser(String userId) {
        USER_CONTEXT_MAP.remove(userId);
    }


    public void addContext(String userId, Channel channel) {
        try {
            String channelId = channel.id().toString();
            AttributeKey attributeKey = null;
            if (!AttributeKey.exists(channelId)) {
                attributeKey = AttributeKey.newInstance(channelId);
            } else {
                attributeKey = AttributeKey.valueOf(channelId);
            }
            channel.attr(attributeKey).set(userId);

            USER_CONTEXT_MAP.put(userId, channel);

            UserInfo userInfo = new UserInfo();
            userInfo.setLastLoginTime(System.currentTimeMillis());
            userInfoMapper.updateByUserId(userInfo, userId);

            TokenUserInfoDto tokenUserInfoDto = redisComponet.getTokenUserInfoDto(userId);
            if (tokenUserInfoDto.getCurrentMeetingId() == null) {
                return;
            }
            //TODO 加入会议
        } catch (Exception e) {
            log.error("初始化连接失败", e);
        }
    }

    /*   *
     * 加入会议室
     *
     * @param meetingId*/

    public void addMeetingRoom(String meetingId, String userId) {
        Channel context = USER_CONTEXT_MAP.get(userId);
        if (null == context) {
            return;
        }
        ChannelGroup group = MEETING_ROOM_CONTEXT_MAP.get(meetingId);
        if (group == null) {
            group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
            MEETING_ROOM_CONTEXT_MAP.put(meetingId, group);
        }
        Channel channel = group.find(context.id());
        if (channel == null) {
            group.add(context);
        }
    }
}
